<?php

namespace Fluency\Web;

/**
 * A configuration container for the RequestDispatcher class which also validates its fields when
 * getters corresponding to them are invoked.
 *
 * @package Fluency\Web
 * @author  Ignas Rudaitis
 * @since   1.0
 */
class RequestDispatcherBuilder
{
    private $actionResolver;
    private $filterResolver;
    private $viewResolver;
    private $globalFilters = array();

    /**
     * Returns a new RequestDispatcher instance with all the parameters that have been set
     * using the builder.
     *
     * @return Fluency\Web\RequestDispatcher
     */
    public function build()
    {
        return new RequestDispatcher($this);
    }

    /**
     * Validates the action resolver for callability and returns it.
     *
     * @see setActionResolver()
     *
     * @throws BadFunctionCallException if the action resolver is not callable
     * @return callable
     */
    public function getActionResolver()
    {
        $this->assertCallable($this->actionResolver, "action resolver");
        return $this->actionResolver;
    }

    /**
     * Sets the action resolver.
     *
     * The action resolver is a function that takes an action name and returns the action
     * corresponding to it. If no such action exists, it should throw an InvalidArgumentException.
     * Note that the action name MUST BE TREATED AS UNTRUSTED INPUT and sanitized properly.
     *
     * @param callable $actionResolver the new action resolver
     * @return Fluency\Web\RequestDispatcherBuilder $this
     */
    public function setActionResolver($actionResolver)
    {
        $this->actionResolver = $actionResolver;
        return $this;
    }

    /**
     * Validates the filter resolver for callability and returns it.
     *
     * @see setFilterResolver()
     *
     * @throws BadFunctionCallException if the filter resolver is not callable
     * @return callable
     */
    public function getFilterResolver()
    {
        $this->assertCallable($this->filterResolver, "filter resolver");
        return $this->filterResolver;
    }

    /**
     * Sets the filter resolver.
     *
     * The filter resolver is a function that takes a filter name and returns the filter
     * corresponding to it. No existence checks are needed for filters. Also, filter names come
     * from action annotations, so they can be treated as trusted input.
     *
     * @param callable $filterResolver the new filter resolver
     * @return Fluency\Web\RequestDispatcherBuilder $this
     */
    public function setFilterResolver($filterResolver)
    {
        $this->filterResolver = $filterResolver;
        return $this;
    }

    /**
     * Validates the view resolver for callability and returns it.
     *
     * @see setViewResolver()
     *
     * @throws BadFunctionCallException if the view resolver is not callable
     * @return callable
     */
    public function getViewResolver()
    {
        $this->assertCallable($this->viewResolver, "view resolver");
        return $this->viewResolver;
    }

    /**
     * Sets the view resolver.
     *
     * The view resolver is a function that two arguments. The first of them is a view name and the
     * second one is a ActiveRequest object. The resolver should return a view corresponding to the
     * given name. A view is a function that renders its argument and returns or echoes (or both)
     * the rendering output. The provided ActiveRequest should be made accessible to the view.
     *
     * @param callable $viewResolver the new view resolver
     * @return Fluency\Web\RequestDispatcherBuilder $this
     */
    public function setViewResolver($viewResolver)
    {
        $this->viewResolver = $viewResolver;
        return $this;
    }

    /**
     * Gets the names of global filters (filters that are applied to all actions) as an array.
     *
     * @return string[]
     */
    public function getGlobalFilters()
    {
        return $this->globalFilters;
    }

    /**
     * Sets the names of global filters (filters that are applied to all actions).
     *
     * @param string[] $globalFilters an array of all the new global filter names
     */
    public function setGlobalFilters(array $globalFilters)
    {
        $this->globalFilters = $globalFilters;
        return $this;
    }

    private function assertCallable($value, $name)
    {
        if (!is_callable($value)) {
            throw new \BadFunctionCallException("No callable {$name} is set");
        }
    }
}
